{***************************************************************************}
{                                                                           }
{           Spring Framework for Delphi                                     }
{                                                                           }
{           Copyright (C) 2009-2011 DevJET                                  }
{                                                                           }
{           http://www.spring4d.org                                         }
{                                                                           }
{***************************************************************************}
{                                                                           }
{  Licensed under the Apache License, Version 2.0 (the "License");          }
{  you may not use this file except in compliance with the License.         }
{  You may obtain a copy of the License at                                  }
{                                                                           }
{      http://www.apache.org/licenses/LICENSE-2.0                           }
{                                                                           }
{  Unless required by applicable law or agreed to in writing, software      }
{  distributed under the License is distributed on an "AS IS" BASIS,        }
{  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
{  See the License for the specific language governing permissions and      }
{  limitations under the License.                                           }
{                                                                           }
{***************************************************************************}

 unit Spring.Configuration.Node experimental;

{$I Spring.inc}

interface

uses
  Classes,
  SysUtils,
  Rtti,
  Spring,
  Spring.Collections,
  Spring.Collections.Lists,
  Spring.Configuration,
  Spring.Configuration.ConfigurationProperty;

type
  TConfiguration = class;

  ///	<summary>Base implementation of IConfiguration interface.</summary>
  TConfiguration = class(TInterfacedObject, IConfiguration)
  strict private
    fName: string;
    fParent: TConfiguration;
    fChildren: IList<IConfiguration>;
    fProperties: IDictionary<string, TConfigurationProperty>;
  {$REGION 'Property Accessors'}
    function GetName: string;
    function GetParent: IConfiguration;
    function GetChildrenList: IList<IConfiguration>;
    function GetChildren: IConfigurations;
    function GetProperties: IDictionary<string, TConfigurationProperty>;
  {$ENDREGION}
  protected
    procedure SetName(const newName: string); virtual;
  public
    constructor Create(const parent: IConfiguration);
    destructor Destroy; override;
    function TryGetProperty(const propertyName: string; out requestedProperty: TConfigurationProperty): Boolean;
    function GetProperty(const propertyName: string): TConfigurationProperty;
    function TryGetChild(const childName: string;
      out requestedChild: IConfiguration): Boolean;
    function GetChild(const childNode: string): IConfiguration;
    property Name: string read GetName;
    property Parent: IConfiguration read GetParent;
    property Properties: IDictionary<string, TConfigurationProperty> read GetProperties;
    function AddChild: IConfiguration;
    property Children: IConfigurations read GetChildren;
  end;

  TConfigurations = class(TList<IConfiguration>,
                          IList<IConfiguration>,
                          IConfigurations)
  end;

  EConfigurationException = class(Exception);

implementation

uses
  Spring.Configuration.ResourceStrings;

{$REGION 'TConfiguration'}

function TConfiguration.AddChild: IConfiguration;
begin
  Result := TConfiguration.Create(Self);
  GetChildrenList.Add(Result);
end;

constructor TConfiguration.Create(const parent: IConfiguration);
begin
  inherited Create;
  fParent := parent as TConfiguration;
end;

destructor TConfiguration.Destroy;
begin
  if Assigned(fProperties) then
    (fProperties as TConfigurationPropertiesDictionary).ParentNodeCollection := nil;
  inherited;
end;

function TConfiguration.TryGetProperty(const propertyName: string;
  out requestedProperty: TConfigurationProperty): Boolean;
begin
  Result := Properties.TryGetValue(propertyName, requestedProperty);
end;

function TConfiguration.GetProperty(const propertyName: string): TConfigurationProperty;
begin
  if not TryGetProperty(propertyName, Result) then
    raise EConfigurationException.CreateResFmt(@SConfigurationAttributeNotFound, [propertyName]);
end;

function TConfiguration.TryGetChild(const childName: string;
  out requestedChild: IConfiguration): Boolean;
var
  sections: IEnumerable<IConfiguration>;
begin
  sections := Children.Where(
    function(const configuration: IConfiguration): Boolean
    begin
      Result := SameText(configuration.Name, childName);
    end
  );
  Result := not sections.IsEmpty;
  if Result then
    requestedChild := sections.First;
end;

function TConfiguration.GetChild(const childNode: string): IConfiguration;
begin
  if not TryGetChild(childNode, Result) then
    raise EConfigurationException.CreateResFmt(@SConfigurationChildrenNotFound, [childNode]);
end;

procedure TConfiguration.SetName(const newName: string);
{var
  nameDuplicated: boolean;
  node: IConfiguration;}
begin
  if newName <> fName then
  begin
    {
    if Assigned(Parent) then
    begin
      nameDuplicated := false;
      for node in Parent.Children do
        if node.Name = newName then
        begin
          nameDuplicated := true;
          Break;
        end;
      if nameDuplicated then
        raise EConfigurationException.CreateResFmt(@SConfigurationNameDuplicated, [newName])
      else
        fName := newName;
    end
    else
    }
      fName := newName;
  end;
end;

function TConfiguration.GetName: string;
begin
  Result := fName;
end;

function TConfiguration.GetParent: IConfiguration;
begin
  Result := fParent;
end;

function TConfiguration.GetProperties: IDictionary<string, TConfigurationProperty>;
begin
  if fProperties = nil then
  begin
    fProperties := TConfigurationPropertiesDictionary.Create;
    if Assigned(Parent) then
      (fProperties as TConfigurationPropertiesDictionary).ParentNodeCollection := Parent.Properties;
  end;
  Result := fProperties;
end;

function TConfiguration.GetChildren: IConfigurations;
begin
  Result := GetChildrenList as IConfigurations;
end;

function TConfiguration.GetChildrenList: IList<IConfiguration>;
begin
  if fChildren = nil then
  begin
    fChildren := TConfigurations.Create;
  end;
  Result := fChildren;
end;

{$ENDREGION}

end.

